package com.ntd.study.modular.dubbo;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.rpc.service.GenericService;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class DubboUtils {
    public static final Map<String, Object> serviceMap = new HashMap<>();

    /**
     * 取得Dubbo bean
     * @param clazz
     * @param version
     * @param <T>
     * @return
     */
    public static <T>  T getService(Class<T> clazz, String version) {
        checkParam(clazz, version);
        String key = StrUtil.concat(Boolean.TRUE, clazz.getName(), ":", version);
        if(serviceMap.containsKey(key)) {
            return getServerByCache(clazz, key);
        }
        ReferenceConfig reference = createReferenceConfig();
        reference.setVersion(version);
        // 弱类型接口名
        reference.setInterface(clazz);
        // 声明为泛化接口
        reference.setGeneric(Boolean.FALSE);
        Object server = reference.get();
        serviceMap.put(key, server);
        return (T)server;
    }

    /**
     * 取得Dubbo Bean
     * @param clazz
     * @param methodName
     * @param version
     * @param paramsClassName
     * @param params
     * @param tag 可以不传 不传的话取system环境变量
     * @return
     */
    public static Object callGenericService(String clazz, String methodName, String version, String[] paramsClassName, Object[] params, String... tag) {
        if(StrUtil.isBlank(clazz) || StrUtil.isBlank(methodName)) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():参数不能为空.packageName="+clazz+",methodName="+methodName);
        }
        if((paramsClassName!=null && params == null) || (paramsClassName==null && params!= null)) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():参数设置错误.paramsClassName="+clazz+",params="+params);
        }
        if((paramsClassName != null && params != null) && (paramsClassName.length != params.length)) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():参数设置错误.paramsClassName="+clazz+",params="+params);
        }
        String key = StrUtil.concat(Boolean.TRUE, clazz, ".", methodName, ":", version, ":gen");
        Object objService = serviceMap.get(key);
        if(objService!=null && !(objService instanceof  GenericService)) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():返回值类型不符.期望:GenericService,实际:"+objService.getClass().getName());
        }
        if(serviceMap.containsKey(key) && objService == null) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():获取远程bean失败.");
        }

        if(objService == null) {
            ReferenceConfig reference = createReferenceConfig();

            reference.setVersion(version);
            // 弱类型接口名
            reference.setInterface(clazz);
            // 声明为泛化接口
            reference.setGeneric("true");

            objService = reference.get();
            serviceMap.put(key, objService);
        }

        // 基本类型以及Date,List,Map等不需要转换，直接调用
        GenericService genericService = (GenericService) objService;
        return genericService.$invoke(methodName, paramsClassName, params);
    }

    private static ReferenceConfig createReferenceConfig() {
        // 普通编码配置方式
        ApplicationConfig application = new ApplicationConfig();
        // 客户端应用名：可以任意取名
        application.setName("dubbo-generic-name");
        application.setLogger("slf4j");

        // 连接注册中心配置
        RegistryConfig registry1 = new RegistryConfig();
        registry1.setAddress("zookeeper://vr-zk-1.189read.com:2181");
        RegistryConfig registry2 = new RegistryConfig();
        registry2.setAddress("zookeeper://vr-zk-2.189read.com:2181");
        List<RegistryConfig> registries = new ArrayList<>();
        registries.add(registry1);
        registries.add(registry2);

        // 引用远程服务
        // 该实例很重量，里面封装了所有与注册中心及服务提供方连接，请缓存
        ReferenceConfig reference = new ReferenceConfig();
        reference.setApplication(application);
        reference.setRegistries(registries);
        reference.setCheck(false);
        return reference;
    }

    private static <T> T getServerByCache(Class<T> clazz, String key) {
        Object server = serviceMap.get(key);
        String clazzName = clazz.getName();
        String cacheClazzName = server.getClass().getName();
        if(ObjectUtil.notEqual(server.getClass().getName(), clazz.getName())) {
            throw new IllegalArgumentException("DubboUtils#getService():返回值类型不符.期望:"+clazzName+
                    ",实际:"+cacheClazzName);
        }
        return (T) server;
    }

    private static <T> void checkParam(Class<T> clazz, String version) {
        if(clazz == null || StrUtil.isBlank(version)) {
            throw new IllegalArgumentException("DubboUtils#getService():参数不能为空.");
        }
    }
}